{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Tutorial 1: Preparation and Execution of Readout Noise Characterization Circuits on IBM Quantum Systems and Amazon Braket Systems\n", "\n", "This tutorial focuses on preparing and executing characterization experiments on real quantum devices.\n", "\n", "We start by preparing a collection of random quantum circuits. Each circuit consists of a single layer of one-qubit gates, randomly selected from a predefined set. In this context, we neglect the errors introduced by these gates.\n", "\n", "In an ideal scenario, we would perform single-copy measurements of states from these random circuits and use the statistical outcomes to characterize the quantum device. However, due to the limitations of current noisy intermediate-scale quantum (NISQ) devices, a more practical approach involves reusing a set of unique quantum circuits (~hundreds) multiple times. This modified protocol is detailed in Appendix A of [Tuziemski, Jan, et al.](http://arxiv.org/abs/2311.10661).\n", "\n", "This tutorial will guide you through the preparation, execution, and analysis of experiments on two leading quantum machine platforms: IBM Quantum Systems and Amazon Braket. The tutorial is structured as follows:\n", "\n", "1. [Preparation of Experiments Characterizing Readout Noise](#Part1)\n", " \n", " 1.1 [The Configuration File](#Part11)\n", "\n", " 1.2 [Importing the Configuration File](#Part12)\n", "\n", "2. [Execution of the Experiment on IBM Quantum System Eagle r2 ](#Part2)\n", "\n", " 2.1 [Initial Steps](#Part21)\n", " \n", " 2.2 [Creating Characterisation Circuits](#Part22)\n", " \n", " 2.3 [Experiment Preparation](#Part23)\n", " \n", " 2.4 [Experiment Execution](#Part24)\n", " \n", " 2.5 [Retrieving Results](#Part25)\n", "\n", "3. [Execution of the Experiment on Amazon Braket Rigetti ASPEN M-3 System](#Part3)\n", "\n", " 2.1 [Initial Steps](#Part31)\n", " \n", " 2.2 [Creating Characterisation Circuits](#Part32)\n", " \n", " 2.3 [Experiment Preparation](#Part33)\n", " \n", " 2.4 [Experiment Execution](#Part34)\n", " \n", " 2.5 [Retrieving Results](#Part35)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## 1. Preparation of Experiments Characterizing Readout Noise\n", "\n", "\n", "\n", "\n", "### 1.1. The Configuration File\n", "\n", "The specifics of the experiment are defined within a QREM '.ini' configuration file. You can find examples of such files in our repository:\n", "- [example configuration file for IBM Quantum Eagle r2 system](https://github.com/cft-nisq/qrem/tree/main/src/qrem/common/default_ibm.ini)\n", "- [example configuration file for AWS Braket ASPEN M-3 system](https://github.com/cft-nisq/qrem/tree/main/src/qrem/common/default_aws.ini)\n", "\n", "The configuration file is organized into sections: 'general', 'data', 'experiment', 'characterization' and 'mitigation'. Here we will cover only the 'general', 'data' and 'experiment' parts:\n", "-**general**: Contains general settings for the experiment, such as the experiment's name, author, and logging level.\n", "\n", "- **data** Manages settings related to data handling, including backups of circuits, job IDs, and circuit metadata.\n", "\n", "- **experiment** Specifies various parameters and settings directly related to the quantum experiment, including device information, provider details, experiment type, and quantum circuit configuration.\n", "\n", "\n", "You can find the full specification of the configuration file in our [documentation](https://cft-nisq.github.io/qrem/autoapi/qrem/common/config/index.html), here we will denote most important parameters from the ponit of view of the experiment design:\n", "\n", "- **experiment_type** : a string specifying a type of readout characterization experiment to be performed. Currently QREM supports two types of characterization experiments Quantum Detector Overlapping Tomography ('QDOT') or Diagonal Detector Overlapping Tomography ('DDOT'). For this tutorial we will choose DDoT.\n", " \n", "- **k_locality** : The locality of the readout noise to consider within the experiment, with values ranging from 2 to 5. We choose 2 fot this tutorial.\n", "\n", "- **gate_threshold** : Gate error threshold, if crossed - qubits will be excluded from calculations. ranging from 0 to 1. A value of 0 or Null includes all qubits, which is the setting we choose here.\n", " \n", "- **limited_circuit_randomness** : For now should be True. Indicates if limitations should be imposed on a number of random circuits configurations (e.g., number of random circuits). For now number of random circuits we can process is limited by technical constrains on available machines (limited execution time, duration of execution)\n", "\n", "-**random_circuits_count** : Total count of random circuits to be sent. We set it to 1500 for this tutorial\n", "\n", "-**random_circuits_count** : Number of shots (repetitions) per unique circuit, we set it to 10000.\n", "\n", "- **experiment_path** : The file path for storing experiment-related files.\n", "\n", "You can read all parameters we will run in this tutorial in the default configuration file [here](https://github.com/cft-nisq/qrem/tree/main/src/qrem/common/default_aws.ini)\n", "\n", "\n", "\n", "### 1.2. Importing the configuration file\n", "\n", "Let's import the default configuration file. If you prepared your version - you can use the commented line to change the source of config file to the file specified by you." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "import qrem\n", "from qrem.common.config import example_config_ibm_path\n", "\n", "CONFIG_PATH = example_config_ibm_path\n", "#CONFIG_PATH = \"path_to_your_ini_file.ini\"]\n", "\n", "config = qrem.load_config(path = CONFIG_PATH, verbose_log = True)\n", "\n", "# Let's read in basic configuration parameters:\n", "EXPERIMENT_NAME = config.experiment_name\n", "\n", "EXPERIMENT_FOLDER_PATH = Path(config.experiment_path)\n", "if not EXPERIMENT_FOLDER_PATH.is_dir():\n", " EXPERIMENT_FOLDER_PATH.mkdir( parents=True, exist_ok=True )\n", "\n", "BACKUP_CIRCUITS = config.backup_circuits\n", "BACKUP_JOB_IDs = config.backup_job_ids\n", "BACKUP_CIRCUITS_METADATA = config.backup_circuits_metadata\n", "JOB_TAGS = list(config.job_tags)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## 2. Execution of the experiments on IBM Quantum System Eagle r2\n", "\n", "### Setting up .env file\n", "\n", "To connect with IBM machine you need to define environmental variables as per provider instructions. With qrem, you can set them up by setting up correctly a .env file in your project path. You can see an example .env file [here](https://github.com/cft-nisq/qrem/blob/main/.env-default). Keep in mind, that if you modify our template, you need to rename it to .env and place in your working directory. \n", "\n", "In .env file: \n", "- QISKIT_IBM_TOKEN = xxxxx # put_your_personal_token_here\n", "- QISKIT_IBM_CHANNEL = ibm-q/open/main # put correct channel here for your organisation, or use open channel\n", "\n", "\n", "### 2.1 Initial steps\n", "\n", "To define how many qubits our circuits will contain, and get the indicies of the valid qubits (not all available systems label the availbale qubits sequentially), we need to comunicate with the IBM Backend. For that puirpose we will use **qrem.providers.ibm** submodule. Let's connect to the backend and find get valid qubits.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qrem.providers import ibm\n", "# ----------------------------------------------------------------\n", "# [1] Get info from provider about valid qubits\n", "# ----------------------------------------------------------------\n", "CONNECTION_METHOD = config.ibm_connection_method\n", "backend, service, provider = ibm.connect(name = config.device_name, method = CONNECTION_METHOD, verbose_log = config.verbose_log)\n", "valid_qubit_properties = ibm.get_valid_qubits_properties(backend, config.gate_threshold)\n", "\n", "number_of_qubits = valid_qubit_properties[\"number_of_good_qubits\"]\n", "good_qubits_indices = valid_qubit_properties[\"good_qubits_indices\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's add some infomration to the metadata object, which will be saved in the experiment folder with circuits collection\n", "\n", "\n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "from qrem.common import io\n", "\n", "METADATA = {}\n", "METADATA[\"date\"] = io.date_time_formatted()\n", "if BACKUP_CIRCUITS_METADATA:\n", " METADATA[\"valid_qubit_properties\"] = valid_qubit_properties" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 2.2 Creating characterisation circuits\n", "\n", "Now we have all necessary info to generate our characterisation circuit collection. It is held by qrem specific data structure: **qrem.qtypes.CircuitCollection**. This implementation is QREM - specific and does not rely on any external packages/implementations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qrem.qtypes import CircuitCollection\n", "from qrem.common.experiment import tomography\n", "from qrem.common.printer import warprint\n", "# ----------------------------------------------------------------\n", "#[1] Generate circuits colelction object and fill in parameters\n", "# ----------------------------------------------------------------\n", "\n", "qrem_circuit_collection = CircuitCollection()\n", "qrem_circuit_collection.experiment_name = EXPERIMENT_NAME\n", "\n", "qrem_circuit_collection.load_config(config=config)\n", "qrem_circuit_collection.device = config.device_name\n", "qrem_circuit_collection.qubit_indices = good_qubits_indices\n", "qrem_circuit_collection.metadata = METADATA\n", "\n", "# ----------------------------------------------------------------\n", "#[1] Generate circuits \n", "# ----------------------------------------------------------------\n", "qrem_circuit_collection.circuits, _, theoretical_total_circuit_count, theoretical_number_of_shots = tomography.generate_circuits( number_of_qubits = number_of_qubits,\n", " experiment_type = config.experiment_type, \n", " k_locality = config.k_locality,\n", " limited_circuit_randomness = config.limited_circuit_randomness,\n", " imposed_max_random_circuit_count = config.random_circuits_count,\n", " imposed_max_number_of_shots = config.shots_per_circuit)\n", " \n", "\n", "if BACKUP_CIRCUITS: \n", " qrem_circuit_collection.export_json(str(EXPERIMENT_FOLDER_PATH.joinpath(\"input_circuit_collection.json\")), overwrite = True)\n", "else:\n", " warprint(\"WARNING: Circuits were not saved to file, as BACKUP_CIRCUITS = False. It is recommended to save circuits to file for future reference.\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using QREM package, you can add special circuits to the experiment, like circuits for benchmarking, circuits containing ground state approximation for benchmarked hamiltonians or coherenve witness circuits. Tutorial how to prepare them and add them to the mix will be prepared in short future." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 2.3 Experiment preparation \n", "\n", "As **qrem.qtypes.CircuitCollection** is a QREM internal object, we will need to translate it to qiskit format, required by the IBM backened for circuit execution. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ibm_circuits = ibm.translate_circuits_to_qiskit_format(qrem_circuit_collection)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 2.4 Experiment execution\n", "\n", "Now we can run circuits on our backend machine. Make always sure that you are using correct IBM instance, and chose correct backend when connecting at the beggining of the process. In this step we will also back up again our CircuitsCollection to json file, together with job ids, so after experiment is ready we will know which job IDs correspond to the sent experiment. In short, every unique quantum circuit will correspond to a separate job and job ID." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import orjson \n", "\n", "#[6] Now we need to run circuits\n", "job_ids = ibm.execute_circuits( qiskit_circuits= ibm_circuits,\n", " job_tags = JOB_TAGS,\n", " number_of_repetitions = config.shots_per_circuit,\n", " instance = config.provider_instance,\n", " service = service,\n", " backend = backend,\n", " method = CONNECTION_METHOD,\n", " log_level='INFO',\n", " verbose_log=True)\n", "\n", "#[6.1] Backup jobs to circuit collection file\n", "if BACKUP_CIRCUITS: \n", " qrem_circuit_collection.job_IDs = job_ids\n", " qrem_circuit_collection.export_json(str(EXPERIMENT_FOLDER_PATH.joinpath(\"input_circuit_collection.json\")), overwrite = True)\n", " \n", "#[6.2] Save job ids to a file\n", "if BACKUP_JOB_IDs: \n", " json_job_ids=orjson.dumps(job_ids)\n", " with open(str(EXPERIMENT_FOLDER_PATH.joinpath(\"job_ids.json\")), 'wb') as outfile:\n", " outfile.write(json_job_ids)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 2.5 Retrieving results\n", "\n", "You can check on IBM Quantum Platform [https://quantum.ibm.com/](https://quantum.ibm.com/), when your jobs will complete. After they are complete, you can retrieve your results, based on the backed up json file with Circuit Collection. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "backend, service, provider = ibm.connect(name = config.device_name, method = CONNECTION_METHOD, verbose_log = config.verbose_log)\n", "valid_qubit_properties = ibm.get_valid_qubits_properties(backend, config.gate_threshold)\n", "\n", "circuit_collection = CircuitCollection()\n", "circuit_collection.load_json(str(EXPERIMENT_FOLDER_PATH.joinpath(\"input_circuit_collection.json\")))\n", "\n", "experiment_results = ibm.retrieve_results( device_name =config.device_name,\n", " provider_instance = config.provider_instance,\n", " job_IDs = circuit_collection.job_IDs,\n", " original_circuits = circuit_collection,\n", " save_experiment_results = str(EXPERIMENT_FOLDER_PATH.joinpath(\"ibm_experiment_results.json\")),\n", " overwrite = False,\n", " verbose_log = True)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can now use the experiment_results object and circuit_collection object in futher part of this tutorial series.\n", "\n", "Below you will find how to connect and run characterisation experiment on Amazon machine (available soon)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## 3. Execution of the Experiments on IBM Quantum System Eagle r2\n", "\n", "### Setting up Amazon Braket Local Development Environment\n", "\n", "To connect with Amazon Braket tou need to set up your local environment for connection with Amazon Braket. To do that please follow steps 1-4 of [Setting up your local development environment in Amazon Braket](https://Amazon.amazon.com/blogs/quantum-computing/setting-up-your-local-development-environment-in-amazon-braket/) tutorial. You don't need to set up conda environment, the virtual environment prepared in installation tutorial will be sufficient and supports Amazon Braket SDK.\n", "\n", "Also, remember to set up relevant parameter in your qrem config file, such as **provider**, **device_name**, **provider_instance**. For the next part of the tutorial, we will use a default ini file for Amazon Braket provided by qrem package. You can get this template from [here](https://github.com/cft-nisq/qrem/tree/main/src/qrem/common/default_aws.ini) and modify yourself. Let's read in the confi file:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "import qrem\n", "from qrem.common.config import example_config_ibm_path\n", "\n", "CONFIG_PATH = example_config_aws_path\n", "#CONFIG_PATH = \"path_to_your_ini_file.ini\"]\n", "\n", "config = qrem.load_config(path = CONFIG_PATH, verbose_log = True)\n", "\n", "# Let's read in basic configuration parameters:\n", "EXPERIMENT_NAME = config.experiment_name\n", "\n", "EXPERIMENT_FOLDER_PATH = Path(config.experiment_path)\n", "if not EXPERIMENT_FOLDER_PATH.is_dir():\n", " EXPERIMENT_FOLDER_PATH.mkdir( parents=True, exist_ok=True )\n", "\n", "BACKUP_CIRCUITS = config.backup_circuits\n", "BACKUP_CIRCUITS_METADATA = config.backup_circuits_metadata\n", "JOB_TAGS = list(config.job_tags)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 3.1 Initial steps\n", "\n", "To define how many qubits our circuits will contain, and get the indicies of the valid qubits (not all available systems label the availbale qubits sequentially), we need to comunicate with the Amazon Braket Backend. For that puirpose we will use **qrem.providers.aws** submodule. Let's connect to the backend and find get valid qubits.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "from qrem.providers import aws_braket\n", "\n", "aws_device, metadata = aws_braket.get_device(device_full_name = config.device_name, verbose_log = config.verbose_log);\n", "valid_qubit_properties = aws_braket.get_valid_qubits_properties(device=aws_device, threshold=None, verbose_log = config.verbose_log)#config.gate_threshold, verbose_log = config.verbose_log)\n", "\n", "number_of_qubits = valid_qubit_properties[\"number_of_good_qubits\"]\n", "good_qubits_indices = valid_qubit_properties[\"good_qubits_indices\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's add some infomration to the metadata object, which will be saved in the experiment folder with circuits collection" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "METADATA = metadata\n", "METADATA[\"date\"] = date_time_formatted()\n", "METADATA[\"JOB_TAGS\"] = JOB_TAGS\n", "if BACKUP_CIRCUITS_METADATA:\n", " METADATA[\"valid_qubit_properties\"] = valid_qubit_properties\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 3.2 Creating characterisation circuits\n", "\n", "Now we have all necessary info to generate our characterisation circuit collection. It is held by qrem specific data structure: **qrem.qtypes.CircuitCollection**. This implementation is QREM - specific and does not rely on any external packages/implementations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qrem.qtypes import CircuitCollection\n", "from qrem.common.experiment import tomography\n", "from qrem.common.printer import warprint\n", "# ----------------------------------------------------------------\n", "#[1] Generate circuits colelction object and fill in parameters\n", "# ----------------------------------------------------------------\n", "qrem_circuit_collection = CircuitCollection()\n", "\n", "qrem_circuit_collection.experiment_name = EXPERIMENT_NAME\n", "qrem_circuit_collection.load_config(config=config)\n", "qrem_circuit_collection.qubit_indices = good_qubits_indices\n", "qrem_circuit_collection.metadata = METADATA\n", "\n", "# ----------------------------------------------------------------\n", "#[1] Generate circuits colelction\n", "# ----------------------------------------------------------------\n", "qrem_circuit_collection.circuits, _, theoretical_total_circuit_count, theoretical_number_of_shots = tomography.generate_circuits( number_of_qubits = number_of_qubits,\n", " experiment_type = config.experiment_type, \n", " k_locality = config.k_locality,\n", " limited_circuit_randomness = config.limited_circuit_randomness,\n", " imposed_max_random_circuit_count = config.random_circuits_count,\n", " imposed_max_number_of_shots = config.shots_per_circuit)\n", " \n", "if BACKUP_CIRCUITS: \n", " qrem_circuit_collection.export_json(str(EXPERIMENT_FOLDER_PATH.joinpath(\"input_circuit_collection.json\")), overwrite = True)\n", "else:\n", " warprint(\"WARNING: Circuits were not saved to file, as BACKUP_CIRCUITS = False. It is recommended to save circuits to file for future reference.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using QREM package, you can add special circuits to the experiment, like circuits for benchmarking, circuits containing ground state approximation for benchmarked hamiltonians or coherenve witness circuits. Tutorial how to prepare them and add them to the mix will be prepared in short future." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 3.3 Experiment preparation \n", "\n", "As **qrem.qtypes.CircuitCollection** is a QREM internal object, we will need to translate it to qiskit format, required by the Amazon Braket backened for circuit execution. \n", "\n", "For connecting with Amazon Braket, we will send additional files to the Amazon Service, thus it is expecially important to prepare a \"Job Submission\" folder in which we will output the files to be saved" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "braket_circuits = aws_braket.translate_circuits_to_braket_format(qrem_circuit_collection,valid_qubit_indices=good_qubits_indices)\n", "\n", "\n", "SUBMISSION_FOLDER_PATH = Path(config.experiment_path).joinpath(\"job_submission\")\n", "\n", "if not SUBMISSION_FOLDER_PATH.is_dir():\n", " EXPERIMENT_FOLDER_PATH.mkdir( parents=True, exist_ok=True )\n", "\n", "total_number_of_circuits = len(qrem_circuit_collection.circuits) \n", "print(f\"Total number of circuits: {total_number_of_circuits} \")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 3.4 Experiment execution\n", "\n", "Now we can run circuits on our backend machine. Make always sure that you are using correct AWS Braket instance and available device, and chose correct backend region when connecting at the beggining of the process when setting up local Amazon Environment." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Now we need to prepare and run circuits\n", "circuits_ready = aws_braket.prepare_cricuits( braket_circuits = braket_circuits,\n", " circuit_collection = qrem_circuit_collection,\n", " good_qubits_indices = good_qubits_indices,\n", " number_of_repetitions = config.shots_per_circuit,\n", " number_of_task_retries = config.aws_braket_task_retries,\n", " experiment_name = EXPERIMENT_NAME,\n", " job_tags = JOB_TAGS,\n", " pickle_submission = config.aws_pickle_results,\n", " metadata = METADATA,\n", " verbose_log = config.verbose_log,\n", " job_dir = SUBMISSION_FOLDER_PATH,\n", " overwrite_output = False)\n", "\n", "\n", "if not circuits_ready:\n", " print(\"ERROR during circuit creation, aborting.\")\n", "else:\n", " aws_braket.execute_circuits( device_name=config.device_name,\n", " pickle_submission = config.aws_pickle_results,\n", " job_dir=SUBMISSION_FOLDER_PATH) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 3.5 Retrieving results\n", "\n", "You can check on AWS Braket Platform [https://aws.amazon.com/console/](https://aws.amazon.com/console/), when your jobs and tasks will complete. After they are complete, you can retrieve your results, based on the backed up json file with Circuit Collection. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import orjson\n", "backend, service, provider = ibm.connect(name = config.device_name, method = CONNECTION_METHOD, verbose_log = config.verbose_log)\n", "valid_qubit_properties = ibm.get_valid_qubits_properties(backend, config.gate_threshold)\n", "\n", "circuit_collection = CircuitCollection()\n", "circuit_collection.load_json(str(EXPERIMENT_FOLDER_PATH.joinpath(\"input_circuit_collection.json\")))\n", "\n", "#task_arns.txt file needs to be downloaded from AWS Braket console after job and tasks are completed! It will be in the results folder at the respective S3 bucket.\n", "circuit_collection.job_IDs = orjson.loads(open(str(EXPERIMENT_FOLDER_PATH.joinpath(\"task_arns.txt\")), 'rb').read())\n", "\n", "experiment_results = aws_braket.retrieve_results( task_ARNs = circuit_collection.job_IDs,\n", " original_circuits = circuit_collection,\n", " save_experiment_results = str(EXPERIMENT_FOLDER_PATH.joinpath(\"ibm_experiment_results.json\")),\n", " overwrite = False,\n", " verbose_log = True)\n" ] } ], "metadata": { "hide_code_all_hidden": true, "kernelspec": { "display_name": "venv_test_4", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" } }, "nbformat": 4, "nbformat_minor": 5 }